﻿using DA6502.Controls;
using System;
using System.IO;
using System.Windows.Forms;

namespace DA6502.IO
{
    /// <summary>
    /// DA6文件访问器
    /// </summary>
    public class DA6Accessor
    {
        #region ==============================| 常量 |==============================

        const int CPU_STATUS_CHUNK_LOCATION = 0x08;
        const int MEMORY_INFO_CHUNK_LOCATION = 0x10;

        #endregion

        #region ==============================| 类属性 |==============================

        private RegisterPanel registers = null;
        private MemoryView memory = null;
        private FrmEntries entries = null;

        public ToolStripProgressBar ProgressBar = null;
        #endregion

        #region ==============================| 类方法 |==============================

        public DA6Accessor(RegisterPanel regs, MemoryView mem, FrmEntries entrs)
        {
            registers = regs;
            memory = mem;
            entries = entrs;
        }

        #endregion

        #region ==============================| 存档 |==============================

        /// <summary>
        /// 储存R6A文件
        /// </summary>
        /// <param name="filePath">文件路径</param>
        public void Save(string filePath)
        {
            BinaryWriter bw = new BinaryWriter(File.Open(filePath, FileMode.Create, FileAccess.Write, FileShare.None));

            //  FileType
            bw.Write(new byte[4] { 0x52, 0x36, 0x41, 0x20 });

            //  FileSize
            bw.Write((uint)(0));

            //  CPU Registers
            bw.Write(registers.PC);
            bw.Write(registers.A);
            bw.Write(registers.X);
            bw.Write(registers.Y);
            bw.Write(registers.S);
            bw.Write(registers.P);
            bw.Write((byte)0x00);   //Reserved01

            //  Memory
            ushort nUnit = 0;
            bw.Write(nUnit);
            for (int i = 0; i < 0x10000; i++)
            {
                if (memory[(ushort)i] == 0x00
                    && memory.GetCellType((ushort)i) == CELL_TYPE.UNKNOWN
                    && memory.GetInstruction((ushort)i).Length == 0
                    && memory.GetComment((ushort)i).Length == 0) continue;

                byte val = memory.ReadByte((ushort)i);
                CELL_TYPE cType = memory.GetCellType((ushort)i);
                string ins = memory.GetInstruction((ushort)i);
                string cmt = memory.GetComment((ushort)i);

                bw.Write((ushort)i);
                bw.Write(val);
                bw.Write((byte)cType);
                bw.Write(ins);
                bw.Write(cmt);
                nUnit++;
            }

            bw.Seek(MEMORY_INFO_CHUNK_LOCATION, SeekOrigin.Begin);
            bw.Write(nUnit);
            bw.Seek(0, SeekOrigin.End);

            //  Entries
            ListView lsv = entries.GetListView();
            bw.Write((ushort)lsv.Items.Count);
            for (int i = 0; i < lsv.Items.Count; i++)
            {
                ushort adr = Convert.ToUInt16(lsv.Items[i].SubItems[0].Text, 16);
                string cmt = lsv.Items[i].SubItems[1].Text;

                bw.Write(adr);
                bw.Write(cmt);
            }

            bw.Close();
        }

        #endregion

        #region ==============================| 读档 |==============================

        /// <summary>
        /// 读取R6A文件
        /// </summary>
        /// <param name="filePath">文件路径</param>
        public void Read(string filePath)
        {
            BinaryReader br = null;
            try
            {
                br = new BinaryReader(File.Open(filePath, FileMode.Open));

                byte[] buf = new byte[4];
                br.Read(buf, 0, buf.Length);

                // { 0x52, 0x36, 0x41, 0x20 }
                if (buf[0] != 0x52 || buf[1] != 0x36 || buf[2] != 0x41 || buf[3] != 0x20)
                {
                    throw new Exception.FileFormatException();
                }

                br.ReadInt32();
                registers.PC = br.ReadUInt16();
                registers.A = br.ReadByte();
                registers.X = br.ReadByte();
                registers.Y = br.ReadByte();
                registers.S = br.ReadByte();
                registers.P = br.ReadByte();
                br.ReadByte();

                ushort nUnit = br.ReadUInt16();
                if (ProgressBar != null)
                {
                    ProgressBar.Maximum = nUnit;
                    ProgressBar.Visible = true;
                }
                for (int i = 0; i < nUnit; i++)
                {
                    ushort ad = br.ReadUInt16();
                    byte val = br.ReadByte();
                    CELL_TYPE cType = (CELL_TYPE)br.ReadByte();
                    string ins = br.ReadString();
                    string cmt = br.ReadString();

                    if (val != 0x00) memory[ad] = val;
                    if (cType != CELL_TYPE.UNKNOWN) memory.SetCellType(ad, cType);
                    if (ins.Length > 0) memory.SetInstruction(ad, ins);
                    if (cmt.Length > 0) memory.SetComment(ad, cmt);

                    if (ProgressBar != null) ProgressBar.Value = i;
                }

                Console.WriteLine(br.BaseStream.Position);
                nUnit = br.ReadUInt16();
                if (ProgressBar != null)
                {
                    ProgressBar.Maximum = nUnit;
                    ProgressBar.Value = 0;
                }
                for (int i = 0; i < nUnit; i++)
                {
                    ushort ad = br.ReadUInt16();
                    string label = br.ReadString();
                    entries.SetEntry(ad, label);

                    if (ProgressBar != null) ProgressBar.Value = i;
                }

                if (ProgressBar != null) ProgressBar.Visible = false;
            }
            catch (System.Exception e)
            {
                throw e;
            }
            finally
            {
                if (br != null) br.Close();
            }

        }

        #endregion
    }
}
